#!/bin/bash
#
# Copyright (c) Authors: https://www.armbian.com/authors
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.

# Functions:
#
# set_io_scheduler
# prepare_board
# add_usb_storage_quirks

# Read in basic OS image information
. /etc/armbian-release

# and script configuration
. /usr/lib/armbian/armbian-common

# set audio
. /usr/lib/armbian/armbian-audio-config

set_io_scheduler() {

	# Convert kernel version to integer
	KERNELID=$(uname -r | awk -F'.' '{print ($1 * 100) + $2}')
	for i in $(lsblk -idn -o NAME | grep -v zram); do
		read ROTATE < /sys/block/$i/queue/rotational
		case ${ROTATE} in
			1) # mechanical drives
				[[ $KERNELID -lt 420 ]] && sched=cfq || sched=bfq
				;;
			0) # flash based
				[[ $KERNELID -lt 420 ]] && sched=noop || sched=none
				;;
			*)
				continue
				;;
		esac
		echo $sched > /sys/block/$i/queue/scheduler
		echo -e "[\e[0;32m ok \x1B[0m] Setting $sched I/O scheduler for $i"
	done

} # set_io_scheduler

prepare_board() {

	CheckDevice=$(for i in /var/log /var /; do findmnt -n -o SOURCE $i && break; done)
	# adjust logrotate configs
	if [[ "${CheckDevice}" == *"/dev/zram"* || "${CheckDevice}" == "armbian-ramlog" ]]; then
		for ConfigFile in /etc/logrotate.d/*; do sed -i -e "s/\/var\/log\//\/var\/log.hdd\//g" "${ConfigFile}"; done
		sed -i "s/\/var\/log\//\/var\/log.hdd\//g" /etc/logrotate.conf
	else
		for ConfigFile in /etc/logrotate.d/*; do sed -i -e "s/\/var\/log.hdd\//\/var\/log\//g" "${ConfigFile}"; done
		sed -i "s/\/var\/log.hdd\//\/var\/log\//g" /etc/logrotate.conf
	fi

	# unlock cpuinfo_cur_freq to be accesible by a normal user
	prefix="/sys/devices/system/cpu/cpufreq"
	for f in $(ls -1 $prefix 2> /dev/null); do
		[[ -f $prefix/$f/cpuinfo_cur_freq ]] && chmod +r $prefix/$f/cpuinfo_cur_freq 2> /dev/null
	done
	# older kernels
	prefix="/sys/devices/system/cpu/cpu0/cpufreq/"
	[[ -f $prefix/cpuinfo_cur_freq ]] && chmod +r $prefix/cpuinfo_cur_freq 2> /dev/null

	# enable compression where not exists
	find /etc/logrotate.d/. -type f | xargs grep -H -c 'compress' | grep 0$ | cut -d':' -f1 | xargs -r -L1 sed -i '/{/ a compress'
	sed -i "s/#compress/compress/" /etc/logrotate.conf

	# Fully replace Armbian's former limited utilization of cpufrequtils package
	# FIXME literally just borrowing the cpufrequtils config file -- should configuration be driven by a new file?
	if [ -r /etc/default/cpufrequtils ] ; then
		. /etc/default/cpufrequtils
		for Cluster in /sys/devices/system/cpu/cpufreq/policy* ; do
			[[ -e "${Cluster}" ]] || break
			grep -q "${GOVERNOR}" "${Cluster}/scaling_available_governors" && [ -w "${Cluster}/scaling_governor" ] && echo "${GOVERNOR}" >"${Cluster}/scaling_governor"
			[ "X${MIN_SPEED}" != "X" ] && [ -w "${Cluster}/scaling_min_freq" ] && echo "${MIN_SPEED}" >"${Cluster}/scaling_min_freq"
			[ "X${MAX_SPEED}" != "X" ] && [ -w "${Cluster}/scaling_max_freq" ] && echo "${MAX_SPEED}" >"${Cluster}/scaling_max_freq"
		done
	fi

	# Tweak ondemand cpufreq governor settings to increase cpufreq with IO load.
	if [ "X${GOVERNOR}" = "Xondemand" ]; then
		cd /sys/devices/system/cpu
		# FIXME - arbitrary globbing range left as a breadcrumb to encourage socratic learning
		# and take on the task of understanding the performance optimizations in general
		# and its use in this script.
		#     
		# Are you the one?  PR's welcome--and encouraged
		#
		# Why loop over these 3 patterns?  Is it for older and newer kernel paradigms?
		# would `for i in cpufreq/ondemand cpu*[0-9]/cpufreq/ondemand` be sufficient? 
		# 
		# for more insights see conversations here:
		# https://github.com/armbian/build/pull/6120
		# https://github.com/armbian/build/pull/6507
		for i in cpufreq/ondemand cpu[0-9]/cpufreq/ondemand cpu[1-9][0-9]/cpufreq/ondemand; do
			if [ -d $i ]; then
				echo 1 > ${i}/io_is_busy
				echo 25 > ${i}/up_threshold
				echo 10 > ${i}/sampling_down_factor
				echo 200000 > ${i}/sampling_rate
			fi
		done
	fi

	local hw_optimization_family
	# IRQ distribution based on $BOARDFAMILY or $BOARD or $BOOT_SOC
	if [[ "${BOARD}" == "rockpro64" || "${BOARD}" == "renegade-elite" || "${BOARD}" == "pinebook-pro" || "${BOARD}" == "station-p1" ]]; then
		hw_optimization_family="rk3399"
	elif [[ "${BOOT_SOC}" == "rk322x" ]]; then
		hw_optimization_family="rk322x"
	else
		hw_optimization_family="${BOARDFAMILY}"
	fi

	case ${hw_optimization_family} in
		cubox | udoo*) # i.MX6 boards: send Ethernet to cpu3, MMC to cpu1/cpu2 (when available)
			echo 2 > /proc/irq/$(awk -F":" "/mmc0/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -1)/smp_affinity 2> /dev/null
			echo 4 > /proc/irq/$(awk -F":" "/mmc1/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -1)/smp_affinity 2> /dev/null
			echo 8 > /proc/irq/$(awk -F":" "/ethernet/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -1)/smp_affinity 2> /dev/null
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			;;
		meson-g12b) # S922X/A311D: ODROID N2, possibly VIM3, cpu0/cpu1 are the little ones
			# MMC on cpu1, USB3 on cpu2, Ethernet on cpu3, rdma on cpu4, vsync on cpu5
			for i in $(awk -F':' '/mmc/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 1 > /proc/irq/$i/smp_affinity_list
			done
			echo 2 > /proc/irq/$(awk -F":" "/xhci-hcd/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 3 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			# mainline kernel might lack those
			[[ -n $(grep "rdma" /proc/interrupts) ]] && echo 4 > /proc/irq/$(awk -F":" "/rdma/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			[[ -n $(grep "osd-vsync" /proc/interrupts) ]] && echo 5 > /proc/irq/$(awk -F":" "/ osd-vsync/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list

			# test fan on Odroid N2 plus - spin it up shortly
			if [[ -f /sys/devices/virtual/thermal/thermal_zone0/trip_point_4_temp ]]; then
				echo 20000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_4_temp
				sleep 1
				echo 65000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_4_temp
			fi

			;;
		mvebu*) # Clearfog/Turris/Helios4/Espressobin: Send network IRQs to cpu1 on both kernels
			for i in $(awk -F':' '/mwlwifi|mvneta|eth0/{print $1}' /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			;;
		odroidc1) # ODROID-C0/C1/C1+
			echo 1 > /proc/irq/$(awk -F":" "/usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			for i in $(awk -F':' '/Mali_/{print $1}' < /proc/interrupts | sed 's/\ //g'); do echo 1 > /proc/irq/${i}/smp_affinity_list; done
			echo 2 > /proc/irq/$(awk -F":" "/usb2/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 3 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			;;
		odroidc2 | meson64) # S905/S905X/S912: both kernels: send eth0 to cpu3, mmc/usb2 to cpu2 and usb1 to cpu1
			# Basics: http://forum.odroid.com/viewtopic.php?f=115&t=8121#p65777
			for i in $(awk -F':' '/sd_emmc|usb2/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 1 > /proc/irq/$i/smp_affinity_list
			done
			echo 2 > /proc/irq/$(awk -F":" "/usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 3 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			;;
		odroidxu4) # ODROID XU3/XU4/HC1/MC1/HC2
			echo 2 > /proc/irq/$(awk -F":" "/:usb2/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb3/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 7 > /proc/irq/$(awk -F":" "/:usb5/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity_list
			echo 16 > /proc/irq/$(awk -F":" "/dw-mci/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | tail -1)/smp_affinity
			echo 32 > /proc/irq/$(awk -F":" "/dw-mci/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -1)/smp_affinity
			for i in $(awk -F':' '/11800000.mali/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 64 > /proc/irq/$i/smp_affinity
			done
			echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
			;;
		rockchip) # RK3288: usb1 on cpu1, usb3 (EHCI) on cpu2, eth0 and GPU on cpu3
			echo 2 > /proc/irq/$(awk -F":" "/usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/usb3/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -n1)/smp_affinity
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			for i in $(awk -F':' '/gpu/{print $1}' /proc/interrupts | sed 's/\ //g'); do
				echo 8 > /proc/irq/$i/smp_affinity
			done
			;;
		rk322x) # RK322x: usb otg on cpu1, usb2,3,4 (EHCI), usb5,6,7 (OHCI) on cpu2, eth and GPU on cpu3
			echo 2 > /proc/irq/$(awk -F":" "/:usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb2/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb3/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb4/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb5/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb6/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/:usb7/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -n1)/smp_affinity
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus

			# Lima in mainline kernel
			echo 8 > /proc/irq/$(awk -F':' '/gp$/{print $1}' /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F':' '/gpmmu/{print $1}' /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F':' '/pp0/{print $1}' /proc/interrupts | sed 's/\ //g')/smp_affinity
			;;
		rockchip64) # Rock64 and Renegade: GPU on cpu1, USB3 on cpu2, Ethernet on cpu3
			for i in $(awk -F':' '/Mali/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/ehci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/ohci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/xhci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 4 > /proc/irq/$i/smp_affinity
			done
			# Most boards get eth0 as main ethernet interface name, but Helios64 doesn't, see below
			_rockchip64_ethernet_interface_name=eth0
			case ${BOARD_NAME} in
				"Helios64")
					for i in $(awk -F":" "/xhci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
						echo 10 > /proc/irq/$i/smp_affinity
					done
					for i in $(awk -F":" "/ahci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
						echo 30 > /proc/irq/$i/smp_affinity
					done
					_rockchip64_ethernet_interface_name=end0
					;;
				*)
					;;
			esac


			# Wait (up to 5s) until eth0/end0 brought up
			for i in {1..5}; do
				grep -q "$_rockchip64_ethernet_interface_name" /proc/interrupts && break
				sleep 1
			done

			for i in $(awk -F":" "/${_rockchip64_ethernet_interface_name}/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 8 > /proc/irq/$i/smp_affinity
			done

			echo 7 > /sys/class/net/${_rockchip64_ethernet_interface_name}/queues/rx-0/rps_cpus
			echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
			echo 32768 > /sys/class/net/${_rockchip64_ethernet_interface_name}/queues/rx-0/rps_flow_cnt
			;;
		rk3399)
			for i in $(awk -F':' '/gpu/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F':' '/dw-mci/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/ehci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/ohci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F":" "/xhci/ {print \$1}" < /proc/interrupts | sed 's/\ //g'); do
				echo 4 > /proc/irq/$i/smp_affinity
			done

			# Wait (up to 5s) until eth0 brought up
			for i in {1..5}; do
				grep -q "eth0" /proc/interrupts && break
				sleep 1
			done

			echo 8 > /proc/irq/$(awk -F":" "/eth0/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
			echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
			for i in $(awk -F':' 'tolower($0) ~ /pcie/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
				echo 16 > /proc/irq/$i/smp_affinity
			done
			# set dmc memory governor to performance with default kernel
			if [ -f /sys/bus/platform/drivers/rockchip-dmc/dmc/devfreq/dmc/governor ]; then
				echo performance > /sys/bus/platform/drivers/rockchip-dmc/dmc/devfreq/dmc/governor
			fi
			case ${BOARD_NAME} in
				"Pinebook Pro")
					echo s2idle > /sys/power/mem_sleep
					;;
			esac
			;;
		s500) # Roseapple Pi/LeMaker Guitar: send USB IRQs to cpu1/cpu2, DMA0 to cpu2 and Ethernet + SD card to cpu3
			echo 2 > /proc/irq/$(awk -F":" "/usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/usb2/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity 2> /dev/null
			echo 4 > /proc/irq/$(awk -F":" "/usb3/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity 2> /dev/null
			echo 4 > /proc/irq/$(awk -F":" "/owl_dma0/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/ethernet_mac/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/sdcard/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			;;
		sun4i | sun5i | rda8810) # only one core, nothing to improve
			:
			;;
		sun6i) # Banana Pi M2: process eth0 on cpu3, SDIO on cpu2, USB on cpu1
			for i in $(awk -F':' '/hcd:usb/{print $1}' /proc/interrupts | sed 's/\ //g'); do
				echo 2 > /proc/irq/$i/smp_affinity
			done
			for i in $(awk -F':' '/sunxi-mmc/{print $1}' /proc/interrupts | sed 's/\ //g'); do
				echo 4 > /proc/irq/$i/smp_affinity
			done
			echo 8 > /proc/irq/$(awk -F":" '/eth0/ {print $1}' < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			;;
		sun7i) # try to redistribute eth0 irq to dedicated core
			echo 2 > /proc/irq/$(awk -F":" '/eth0/ {print $1}' < /proc/interrupts | sed 's/\ //g')/smp_affinity 2> /dev/null
			;;
		sun8i*) # H3/R40/V40 boards, try to do the best based on specific board since interfaces vary a lot
			# 10 or 120 sec user feedback that the board is ready after 1st login with 3.4 kernel
			SwapState="$(grep swap /etc/fstab)"
			if [ "X${SwapState}" != "X" ]; then
				(echo heartbeat > /sys/class/leds/*green*/trigger) 2> /dev/null
				[ -f "/root/.not_logged_in_yet" ] && BlinkTime=120 || BlinkTime=10
				(sleep ${BlinkTime} && (echo default-on > /sys/class/leds/*green*/trigger) 2> /dev/null) &
			fi

			# check kernel version for IRQ/module names
			case ${KERNELID} in
				3*)
					# BSP kernel
					GbE="gmac0"
					WiFi="wlan0"
					USB1="usb2"
					USB2="usb3"
					USB3="usb4"
					;;
				*)
					# Mainline kernel
					GbE="eth0"
					WiFi="wlan0"
					USB1="usb3"
					USB2="usb4"
					USB3="usb5"
					;;
			esac
			# Assign 1st and 2nd USB port to cpu1 and cpu2 on every sun8i board
			echo 2 > /proc/irq/$(awk -F":" "/${USB1}/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/${USB2}/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			case ${BOARD_NAME} in
				"Orange Pi+" | "Orange Pi+ 2" | "Orange Pi+ 2E" | "Banana Pi M2*" | "NanoPi M1 Plus")
					# Send GBit Ethernet IRQs to cpu3
					echo 8 > /proc/irq/$(awk -F":" "/${GbE}/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
					echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
					;;
				"NanoPi M1" | "Orange Pi PC Plus" | "Orange Pi PC +" | "Orange Pi PC" | "NanoPi Neo" | "Orange Pi Zero")
					# Send 3rd USB port's IRQs to cpu3
					echo 8 > /proc/irq/$(awk -F":" "/${USB3}/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
					;;
				"Orange Pi Lite" | "NanoPi Air" | "Lime A33" | "Orange Pi Zero Plus 2"*)
					# Send SDIO/mmc IRQs to cpu3
					for i in $(awk -F':' '/sunxi-mmc/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
						echo 8 > /proc/irq/$i/smp_affinity
					done
					;;
				"Beelink X2" | "Orange Pi R1" | "ZeroPi")
					# Wifi module reload workaround / fix
					[[ -n $(lsmod | grep 8189es) && "${BOARD_NAME}" != "ZeroPi" ]] && rmmod 8189es && modprobe 8189es
					# Send SDIO to cpu1, USB to cpu2, Ethernet to cpu3
					for i in $(awk -F':' '/sunxi-mmc/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
						echo 2 > /proc/irq/$i/smp_affinity
					done
					for i in $(awk -F':' '/hcd:usb/{print $1}' < /proc/interrupts | sed 's/\ //g'); do
						echo 4 > /proc/irq/$i/smp_affinity
					done
					echo 8 > /proc/irq/$(awk -F":" "/${GbE}/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
					;;
			esac
			;;
		sun50i* | sunxi64*) # A64/H5 based boards like Pine64, OPi PC 2, NanoPi NEO 2
			# Send IRQs for the lower real USB port (usb2) to cpu2 and for the upper (OTG/usb1) to cpu1
			echo 2 > /proc/irq/$(awk -F":" "/usb1/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 4 > /proc/irq/$(awk -F":" "/usb2/ {print \$1}" < /proc/interrupts | sed 's/\ //g')/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/sunxi-mmc/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -n1)/smp_affinity
			echo 8 > /proc/irq/$(awk -F":" "/eth/ {print \$1}" < /proc/interrupts | sed 's/\ //g' | head -n1)/smp_affinity
			echo 7 > /sys/class/net/eth0/queues/rx-0/rps_cpus
			# OrangePi win GMAC is very unstable on gigabit. Limit it down to 100Mb solve problems
			[[ $BOARD == orangepiwin && $BRANCH == default ]] && ethtool -s eth0 speed 100 duplex full
			;;
	esac
} # prepare_board

add_usb_storage_quirks() {

	# check for /boot/armbianEnv.txt existence
	[ -f /boot/armbianEnv.txt ] || return

	# cleanup. add LF. This prevents adding parameters to the same line
	echo "" >> /boot/armbianEnv.txt
	sed -i '/^$/d;$G' /boot/armbianEnv.txt
	sed -i '/^$/d;$G' /boot/armbianEnv.txt

	# cleanup. remove empty lines in the middle
	sed -i '/^$/d' /boot/armbianEnv.txt

	# preserve old contents if existent
	TMPFILE=$(mktemp /tmp/${0##*/}.XXXXXX)
	trap "sleep 1 ; rm \"${TMPFILE}\" ; exit 0" 0 1 2 3 15
	awk -F"=" '/^usbstoragequirks/ {print $2}' < /boot/armbianEnv.txt | tr -d -c '[:graph:]' > ${TMPFILE}

	# UAS blacklist Norelsys NS1068X and NS1066X since broken. Can be removed once
	# they're blacklisted upstream
	[ -s ${TMPFILE} ] || echo "0x2537:0x1066:u,0x2537:0x1068:u" > ${TMPFILE}

	# check for connected Seagate or WD HDD enclosures and blacklist them all
	lsusb | awk -F" " '{print "0x"$6}' | sed 's/:/:0x/' | sort | uniq | while read; do
		case ${REPLY} in
			"0x0bc2:"* | "0x1058:"*)
				grep -q "${REPLY}" ${TMPFILE} || sed -i "1 s/\$/,${REPLY}:u/" ${TMPFILE}
				;;
		esac
	done

	read USBQUIRKS < ${TMPFILE}
	sed -i '/^usbstoragequirks/d' /boot/armbianEnv.txt
	echo "usbstoragequirks=${USBQUIRKS}" >> /boot/armbianEnv.txt
	sync &
	if [ -f /sys/module/usb_storage/parameters/quirks ]; then
		echo ${USBQUIRKS} > /sys/module/usb_storage/parameters/quirks
	fi

} # add_usb_storage_quirks

update_branch_from_installed_kernel() {

	BRANCH=$(dpkg -l | grep -E "linux-image" | grep -E "current|legacy|edge" | awk '{print $2}' | cut -d"-" -f3 | head -1)
	if grep -q BRANCH /etc/armbian-release; then
		[[ -n ${BRANCH} ]] && sed -i "s/BRANCH=.*/BRANCH=$BRANCH/g" /etc/armbian-release
	else
		[[ -n ${BRANCH} ]] && echo "BRANCH=$BRANCH" >> /etc/armbian-release
	fi
}

case $1 in
	*start*)
		# set optimal disk scheduler settings
		set_io_scheduler &

		# hardware preparation
		prepare_board &

		# add usb quirks
		add_usb_storage_quirks &

		# update branch in main config
		update_branch_from_installed_kernel &
		;;
esac
